package com.rainbow.support.half.message.handler.producer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.rainbow.common.core.toolkit.SpringBeanUtil;
import com.rainbow.support.half.message.entity.HalfMessagePayload;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * rocketMq半事务生产者
 * 桥接半事务消息和自定义业务处理综合类
 * @author 傅为地
 */
@Slf4j
public abstract class BridgingProducerHandler implements HalfMsgProducerHandler, InitializingBean{

    //标记生产者业务方法是否使用了@Transactional注解
    private boolean isMethodTransactional = false;

    /**
     * 不同的生产者本地事务处理逻辑,具体处理自己本地事务逻辑
     *
     * @param producer 生产者参数
     * @return 返回生产者本地业务处理结果
     */
    public abstract boolean producerProcess(@NonNull HalfMessagePayload producer);

    /**
     * 本地业务逻辑处理之前,需要查询好需要处理的参数信息
     * 本地事务处理时,直接处理业务逻辑,解决大事务的问题
     *
     * @param producer 生产者参数
     */
    public abstract void prepareParams(@NonNull HalfMessagePayload producer);

    /**
     * 生产者处理,事务逻辑
     * 开启事务在本地处理之前,建议先将需要查询的参数准备好,然后在处理业务数据
     * 不推荐采用此方法处理生产者,消息体内日期格式可能存在问题
     * 推荐采用{@link #process(HalfMessagePayload producer)}
     *
     * @param message 生产者参数
     * @return 返回生产者处理结果
     */
    @Deprecated
    @Override
    public boolean process(@NonNull String message) {
        log.info("RocketMq半事务生产者,接收参数==>producer:{}", message);
        HalfMessagePayload producer = parseHalfMessagePayload(message);
        prepareParams(producer);
        return serviceProcessWapper(producer, message);
    }

    /**
     * 生产者处理,事务逻辑
     * 开启事务在本地处理之前,建议先将需要查询的参数准备好,然后在处理业务数据
     *
     * @param producer 生产者参数
     * @return 返回生产者处理结果
     */
    @Override
    public boolean process(@NonNull HalfMessagePayload producer) {
        String message = JSON.toJSONStringWithDateFormat(producer, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue);
        log.info("RocketMq半事务生产者,接收参数==>producer:{}", message);
        Assert.notNull(producer, "RocketMq半事务生产者,接收参数有误,请检查数据!");
        Assert.notNull(producer.getGlobalId(), "RocketMq半事务生产者,全局事务编号不能为空,请检查数据!");
        Assert.notNull(producer.getBranchId(), "RocketMq半事务生产者,分支事务编号不能为空,请检查数据!");
        Assert.notNull(producer.getOriginBody(), "RocketMq半事务生产者,事务消息体不能为空,请检查数据!");
        Assert.notNull(producer.getCreator(), "RocketMq半事务生产者,消息发送人不能为空,请检查数据!");
        Assert.notNull(producer.getCreateTime(), "RocketMq半事务生产者,消息发送时间不能为空,请检查数据!");
        prepareParams(producer);
        return serviceProcessWapper(producer, message);
    }

    /**
     * 框架自动适配,如果业务方法使用了@Transactional注解,
     * 则使用注解式事务处理,否则使用编程式事务处理,尽量避免事务使用方式不一致情况
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        try {
            Method method = this.getClass().getMethod("producerProcess", HalfMessagePayload.class);
            isMethodTransactional = method.isAnnotationPresent(Transactional.class);
            /*String clazName=method.getDeclaringClass().getSimpleName();
            if (isMethodTransactional) {
                log.warn("半事务生产者"+clazName+".producerProcess()使用了@Transactional注解,框架将使用注解式事务处理");
            } else {
                log.warn("半事务生产者"+clazName+".producerProcess()未使用@Transactional注解,框架将使用编程式事务处理");
            }*/
        } catch (Exception e) {
            log.error("半事务生产者检测producerProcess事务注解时异常", e);
        }
    }

    /**
     * 本地业务处理包装类,根据具体业务处理方法采用的模式,
     * 自动适配框架事务处理模式,兼容业务事务处理模式
     *
     * @param producer 生产者参数
     * @param message  源消息信息
     * @return 处理结果
     */
    public boolean serviceProcessWapper(@NonNull HalfMessagePayload producer, @NonNull String message) {
        return isMethodTransactional ? serviceProcessWithAnotation(producer, message) : serviceProcessWithTemplate(producer, message);
    }

    /**
     * 本地业务处理,采用spring注解式事务,完成本地事务控制和处理
     *
     * @param producer 生产者参数
     * @param message  源消息信息
     * @return 本地事务处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean serviceProcessWithAnotation(@NonNull HalfMessagePayload producer, @NonNull String message) {
        saveProducerLog(producer, message);
        producerProcess(producer);
        return true;
    }

    /**
     * 本地业务处理,采用spring编程式事务,完成本地事务控制和处理
     *
     * @param producer 生产者参数
     * @param message  源消息信息
     * @return 本地事务处理结果
     */
    public boolean serviceProcessWithTemplate(@NonNull HalfMessagePayload producer, @NonNull String message) {
        //使用编程式事务
        Boolean result = Objects.requireNonNull(SpringBeanUtil.getBean(TransactionTemplate.class), "事务组件初始化错误").execute(transactionStatus -> {
            try {
                saveProducerLog(producer, message);
                producerProcess(producer);
                return true;
            } catch (Exception error) {
                log.error("RocketMq生产者处理业务异常:", error);
                if (ObjectUtils.isNotEmpty(transactionStatus)) {
                    transactionStatus.setRollbackOnly();
                }
                throw error;
            }
        });
        return ObjectUtils.isEmpty(result) || result;
    }

}
